package com.xiaoyy.core.oplog.config.aspect;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.system.HostInfo;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.xiaoyy.core.common.cache.CacheConst;
import com.xiaoyy.core.common.enums.BoolEnum;
import com.xiaoyy.core.common.enums.EquipmentTypeEnum;
import com.xiaoyy.core.common.enums.ErrorEnum;
import com.xiaoyy.core.common.enums.SysPermissionEnum;
import com.xiaoyy.core.common.vo.BasePageVO;
import com.xiaoyy.core.common.vo.BaseVO;
import com.xiaoyy.core.common.vo.OplogApiVO;
import com.xiaoyy.core.common.vo.SessionInfo;
import com.xiaoyy.core.config.annotations.OpApi;
import com.xiaoyy.core.common.exception.AppException;
import com.xiaoyy.core.config.store.SessionHelper;
import com.xiaoyy.core.config.util.RedisUtil;
import com.xiaoyy.core.config.util.ServiceRequestUtil;
import com.xiaoyy.core.oplog.integration.OplogIntegration;
import com.xiaoyy.core.oplog.service.OplogService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;
import java.util.Set;

/***
 *日志处理切面
 * chenzh
 * 2020-01-15
 * */
@Aspect
@Component
public class OplogApiAspect {
    private static final Logger logger = LoggerFactory.getLogger(OplogApiAspect.class);

    @Autowired
    OplogIntegration oplogIntegration;
    @Autowired
    OplogService oplogService;
    @Autowired
    RedisUtil redisUtil;
    /**
     * 是否保存请求到数据中
     */
    @Value("${system.config.saveOplogApi}")
    private Boolean saveOplogApi;
    @Value("${system.config.token.timeout}")
    private Long timeout;
    @Value("${system.config.token.app.timeout}")
    private Long appTimeout;
    @Around("execution(* com.xiaoyy..*.integration..*.*(..))&&@annotation(opApi)")
    public Object doAround(ProceedingJoinPoint joinPoint, OpApi opApi) throws Throwable {
        if (saveOplogApi==null){
            saveOplogApi = true;
        }
        OplogApiVO oplogApiVO = new OplogApiVO();
        oplogApiVO.setBegTime(new Date());
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            if (request != null) {
                String ip = ServletUtil.getClientIP(request);
                String api = request.getServletPath();
                oplogApiVO.setIp(ip);
                oplogApiVO.setReqApi(api);
                oplogApiVO.setServerPort(request.getLocalPort());
                oplogApiVO.setServletContext(request.getContextPath().replace("/", ""));
            }
        }catch (Exception e){

        }
        //获取目标对象对应的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取目标对象上正在执行的方法名
        String methodName = joinPoint.getSignature().getName();
        oplogApiVO.setClassPath(className);
        oplogApiVO.setMethod(methodName);
        oplogApiVO.setTitle(opApi.title());
        oplogApiVO.setFuncCode(opApi.funcCode());
        oplogApiVO.setFuncType(opApi.funcType().getType());
        oplogApiVO.setFuncTypeName(opApi.funcType().getText());
        try {
            oplogApiVO.setServerIp(ServiceRequestUtil.getLocalIP());
        }catch (Exception e) {

        }
        HostInfo hostInfo = SystemUtil.getHostInfo();
        if (hostInfo!=null){
            if (StringUtils.isBlank(oplogApiVO.getServerIp())){
                oplogApiVO.setServerIp(hostInfo.getAddress());
            }
            if (StringUtils.isBlank(oplogApiVO.getServletContext())) {
                oplogApiVO.setServletContext(hostInfo.getName());
            }
        }
        Object[] args = joinPoint.getArgs();

        Object ret ;
        try{
            oplogApiVO.setCode("0");
            oplogApiVO.setMessage("成功");
            if (args != null && args.length > 0) {
                if (args.length>1){
                    oplogApiVO.setReqBody(JSON.toJSONString(args));
                }else{
                    oplogApiVO.setReqBody(JSON.toJSONString(args[0]));
                }

                for (int i = 0; i < args.length; i++) {
                    Object obj = args[i];
                    if (obj instanceof BaseVO) {
                        BaseVO baseVO = (BaseVO) obj;
                        oplogApiVO.setRouterId(baseVO.getOpRouterId());
                        oplogApiVO.setFuncId(baseVO.getOpFuncId());
                        oplogApiVO.setAccessToken(baseVO.getAccessToken());
                        oplogApiVO.setOplogId(baseVO.getOpOplogId());
                        if (StringUtils.isBlank(oplogApiVO.getIp())){
                            oplogApiVO.setIp(baseVO.getReqIp());
                        }
                        break;
                    }else if (obj instanceof BasePageVO) {
                        BasePageVO basePageVO = (BasePageVO) obj;
                        oplogApiVO.setRouterId(basePageVO.getOpRouterId());
                        oplogApiVO.setFuncId(basePageVO.getOpFuncId());
                        oplogApiVO.setAccessToken(basePageVO.getAccessToken());
                        oplogApiVO.setOplogId(basePageVO.getOpOplogId());
                        if (StringUtils.isBlank(oplogApiVO.getIp())){
                            oplogApiVO.setIp(basePageVO.getReqIp());
                        }

                        break;
                    }
                }


            }else{
                throw new AppException("操作日志必须传入继承BaseReq的请求实体类");
            }
            if (opApi.publicFlag().getType().equals(BoolEnum.FALSE.getType())){
                // token校验
                String accessToken = oplogApiVO.getAccessToken();
                if (StringUtils.isBlank(accessToken)){
                    throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_LONIN);
                }
                String redisKey= CacheConst.ACCESS_TOKEN+":"+accessToken;
                if (!redisUtil.hasKey(redisKey)){
                    throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_LONIN);
                }
                SessionInfo sessionInfo = (SessionInfo) redisUtil.get(redisKey);
                if (System.currentTimeMillis()-sessionInfo.getExpireFreshTime()>sessionInfo.getExpireMinute()){
                    throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_LONIN);
                }
                if (EquipmentTypeEnum.PC.getType().equals(sessionInfo.getEquipmentType())){
                    if (System.currentTimeMillis()-sessionInfo.getExpireFreshTime()<sessionInfo.getExpireMinute()/2){
                        //pc端系统 过期时间过了一半，有请求的话，刷新
                        sessionInfo.setExpireFreshTime(System.currentTimeMillis());
                        redisUtil.set(redisKey,sessionInfo,timeout);
                    }
                }

                SessionHelper.setSessionInfo(sessionInfo);
                // 权限校验
                boolean checkPrivilege=sessionInfo.isCheckPrivilege();
                if (checkPrivilege) {
                    //权限校验
                    Set<String> systemPrivileges = sessionInfo.getSystemPrivileges();
                    if (!CollectionUtils.isEmpty(systemPrivileges)) {
                        if (systemPrivileges.contains(SysPermissionEnum.superadmin.getType())) {
                            //超级管理员 不控制权限
                            checkPrivilege = false;
                        }
                        if (systemPrivileges.contains(SysPermissionEnum.allfunction.getType())) {
                            //所有功能可操作
                            checkPrivilege = false;
                        }
                    }
                }
                if (checkPrivilege){
                    if (StringUtils.isNotBlank(oplogApiVO.getRouterId())){
                        // 管理系统会传入路由，权限根据路由下配置
                        Map<String, Set<String>> routers =sessionInfo.getRouters();
                        Set<String> funcCodeList = routers.get(oplogApiVO.getRouterId());
                        if (CollectionUtils.isEmpty(funcCodeList)) {
                            throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_PRIVILEGE);
                        }
                        if (!funcCodeList.contains(oplogApiVO.getFuncCode())) {
                            throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_PRIVILEGE);
                        }
                    }else{
                        Set<String> apiList = sessionInfo.getApiList();
                        if (CollectionUtils.isEmpty(apiList)){
                            throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_PRIVILEGE);
                        }

                        if (!apiList.contains(oplogApiVO.getFuncCode())) {
                            throw new AppException(ErrorEnum.SYSTEM_ERROR_NO_PRIVILEGE);
                        }
                    }
                }


            }
            ret = joinPoint.proceed();
            oplogApiVO.setResBody(JSON.toJSONString(ret));
        } catch (AppException e){
            if (e.getCode()!=null){
                oplogApiVO.setCode(e.getCode().toString());
            }else{
                oplogApiVO.setCode("-1");
            }
            oplogApiVO.setMessage(e.getMsg());
            throw e;
        } catch (Exception e){
            oplogApiVO.setCode("-1");
            oplogApiVO.setMessage(e.getMessage());
            throw e;
        }finally {
            oplogApiVO.setEndTime(new Date());
            if (saveOplogApi) {
                oplogIntegration.saveApiOpLog(oplogApiVO);
            }
        }
        return ret;
    }

}
